/*
 * Atomic Predicates Verifier
 *
 * Copyright (c) 2013 UNIVERSITY OF TEXAS AUSTIN. All rights reserved. Developed
 * by: HONGKUN YANG and SIMON S. LAM http://www.cs.utexas.edu/users/lam/NRL/
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * with the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimers.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimers in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the UNIVERSITY OF TEXAS AUSTIN nor the names of the
 * developers may be used to endorse or promote products derived from this
 * Software without specific prior written permission.
 *
 * 4. Any report or paper describing results derived from using any part of this
 * Software must cite the following publication of the developers: Hongkun Yang
 * and Simon S. Lam, Real-time Verification of Network Properties using Atomic
 * Predicates, IEEE/ACM Transactions on Networking, April 2016, Volume 24, No.
 * 2, pages 887-900 (first published March 2015, Digital Object Identifier:
 * 10.1109/TNET.2015.2398197).
 *
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH
 * THE SOFTWARE.
 */

package common;

import ap.APComputer;
import jdd.bdd.BDD;

import java.io.*;
import java.net.ServerSocket;
import java.util.*;

public class Utility {
	private static Vector<Integer> ZeroVector = null;


	public static Vector<Integer> getZeroVector(){
		if(ZeroVector == null){
			Vector<Integer> t = new Vector<>(1);
			t.add(0);
			ZeroVector = t;
		}
		return ZeroVector;
	}

	public static Vector<Integer> getOneNumVector(int num){
		Vector<Integer> t = new Vector<>(1);
		t.add(num);
		return t;
	}

	/*
	 * only used to compute no more than 2^16
	 */
	public static long Power2(int exponent)
	{
		if(exponent <=16)
		{
			switch(exponent){
				case 0: return 1;
				case 1: return 2;
				case 2: return 4;
				case 3: return 8;
				case 4: return 16;
				case 5: return 32;
				case 6: return 64;
				case 7: return 128;
				case 8: return 256;
				case 9: return 512;
				case 10: return 1024;
				case 11: return 2048;
				case 12: return 4096;
				case 13: return 8192;
				case 14: return 16384;
				case 15: return 32768;
				case 16: return 65536;
				default: System.err.println("exponent is too large!");
					break;
			}
		}
		else
		{
			long power = 1;
			for(int i = 0; i < exponent; i ++)
			{
				power = power * 2;
			}
			return power;
		}
		// should not be here
		return 0;
	}

	public interface Callback{
		void execute(String s);
	}
	/*
	 * ex1 < ex2
	 * return 2^ex1 + 2^(ex1+1) + ... + 2^ex2
	 */
	public static long SumPower2(int ex1, int ex2)
	{
		long sum = 0;
		for(int i = ex1; i <= ex2; i ++)
		{
			sum = sum + Power2(i);
		}
		return sum;
	}

	public static Object LoadObject(String filename)
	{

		FileInputStream fis = null;
		ObjectInputStream in = null;
		Object obj = null;
		try
		{
			fis = new FileInputStream(filename);
			in = new ObjectInputStream(fis);
			obj = (Object)in.readObject();
			in.close();
		}
		catch(IOException ex)
		{
			ex.printStackTrace();
		}
		catch(ClassNotFoundException ex)
		{
			ex.printStackTrace();
		}
		System.out.println(filename + " is loaded.");
		return obj;
	}



	/**
	 * the input num is at most bits long
	 * find the number of tailing zeros in the binary representation of num
	 * for example, num = 6 = (110)_2, bits = 3, return 1 
	 */
	public static int tailingZeros(long num, int bits)
	{
		if(num == 0) return bits;
		int tmptailing = 0;
		for(int i = 0; i <bits; i ++)
		{
			long tester = Power2(i) - 1;
			long tn = num & tester;
			if(tn == 0)
			{
				tmptailing = i;
			}else
			{
				break;
			}

		}
		return tmptailing;
	}

	/**
	 * return the binary representation of num
	 * e.g. num = 10, bits = 4, return an array of {0,1,0,1}
	 */
	public static int[] CalBinRep(long num, int bits)
	{
		if(bits == 0) return new int[0];

		int [] binrep = new int[bits];
		long numtemp = num;
		for(int i = bits; i >0; i--)
		{
			long abit = numtemp & Power2(i - 1);
			if(abit == 0)
			{
				binrep[i - 1] = 0;
			}else
			{
				binrep[i - 1] = 1;
			}
			numtemp = numtemp - abit;
		}
		return binrep;
	}

	/**
	 *
	 * @param ar an array
	 * @return number of nonzero element in the array
	 */
	public static int NumofNonZeros(int [] ar)
	{
		int sum = 0;
		for(int i = 0; i < ar.length; i ++)
		{
			if(ar[i] != 0)
			{
				sum ++;
			}
		}
		return sum;
	}

	/**
	 * creat one prefix from [l, u]
	 */
	public static long OnePrefix(long l, long u, int bits, LinkedList<int []> prefixs)
	{
		int zeros = tailingZeros(l, bits);
		if(zeros == 0)
		{
			prefixs.add(CalBinRep(l, bits));
			return l + 1;
		}else
		{
			while(l + Power2(zeros) > u + 1)
			{
				zeros --;
			}
			prefixs.add(CalBinRep(l/Power2(zeros), bits - zeros));
			return l + Power2(zeros);
		}
	}

	public static int[] ArrayListToArray(ArrayList<Integer> intarylist)
	{
		int[] intary = new int[intarylist.size()];
		for(int i = 0; i < intarylist.size(); i ++)
		{
			intary[i] = intarylist.get(i);
		}
		return intary;
	}
	/**
	 * 将 ip 字符串转换为 int 类型的数字
	 * <p>
	 * 思路就是将 ip 的每一段数字转为 8 位二进制数，并将它们放在结果的适当位置上
	 *
	 * @param ipString ip字符串，如 127.0.0.1
	 * @return ip字符串对应的 int 值
	 */
	public static int ip2Int(String ipString) {
		// 取 ip 的各段
		String[] ipSlices = ipString.split("\\.");
		int rs = 0;
		for (int i = 0; i < ipSlices.length; i++) {
			// 将 ip 的每一段解析为 int，并根据位置左移 8 位
			int intSlice = Integer.parseInt(ipSlices[i]) << 8 * i;
			// 求与
			rs = rs | intSlice;
		}
		return rs;
	}

	/**
	 * 将 ipv4 字符串转换为 byte[] 类型的数字
	 *
	 * @param ipString ip字符串，如 127.0.0.1
	 * @return ip字符串对应的 byte[] 值
	 */
	public static byte[] ip2Bytes(String ipString) {
		byte[] ip = new byte[4];
		// 取 ip 的各段
		String[] ipSlices = ipString.split("\\.");
		int rs = 0;
		for (int i = 0; i < ipSlices.length; i++) {
			ip[i] = (byte) Short.parseShort(ipSlices[i]);
		}
		return ip;
	}

	/**
	 * 将 int 转换为 ip 字符串
	 *
	 * @param ipInt 用 int 表示的 ip 值
	 * @return ip字符串，如 127.0.0.1
	 */
	public static String int2Ip(int ipInt) {
		String[] ipString = new String[4];
		for (int i = 0; i < 4; i++) {
			// 每 8 位为一段，这里取当前要处理的最高位的位置
			int pos = i * 8;
			// 取当前处理的 ip 段的值
			int and = ipInt & (255 << pos);
			// 将当前 ip 段转换为 0 ~ 255 的数字，注意这里必须使用无符号右移
			ipString[i] = String.valueOf(and >>> pos);
		}
		return String.join(".", ipString);
	}
	public static Integer getPort()  {
		//读取空闲的可用端口
		ServerSocket serverSocket = null;
		try {
			serverSocket = new ServerSocket(0);
			int port = serverSocket.getLocalPort();
			serverSocket.close();
			return port;
		} catch (IOException e) {
			e.printStackTrace();
		}
		return 0;

	}

	public static String IntegerArrayJoin(String delimiter, Iterable<Integer> arr){
		StringBuilder result = new StringBuilder();
		for (Integer integer : arr) {
			result.append(integer.toString()).append(delimiter);
		}
		return result.toString();
	}

	public static int getDropPredicate(APComputer apc){
		HashSet<Integer> pd = apc.getAllAP();
		pd.removeAll(apc.getPredicates());
		return (int) pd.toArray()[0];
	}
	public static String getFileLastLine(String filePath){
		RandomAccessFile raf;
		String lastLine = "";
		try {
			raf = new RandomAccessFile(filePath, "r");
			long len = raf.length();
			if (len != 0L) {
				long pos = len - 1;
				while (pos > 0) {
					pos--;
					raf.seek(pos);
					if (raf.readByte() == '\n') {
						lastLine = raf.readLine();
						break;
					}
				}
			}
			raf.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return lastLine;
	}

	public static void writeFileLastLine(String path, String str){
		try {
			FileOutputStream fos=new FileOutputStream(path,true);
			fos.write(str.getBytes());
			fos.flush();
			fos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	public static String trimInvalidChar(String s) {
		int len = s.length();
		int st = 0;
		char[] val = new char[len];
		s.getChars(0, len, val, 0);    /* avoid getfield opcode */

		while ((st < len) && ((int) val[st] <= 0 || (int) val[st] > 127)){
			st++;
		}
		while ((st < len) &&  ((int) val[len - 1] <= 0 || (int) val[len - 1] > 127)) {
			len--;
		}
		return ((st > 0) || (len < s.length())) ? s.substring(st, len) : s;
	}



	public static void printCount(HashMap<Integer, Vector<Integer>> table, BDD bdd){
		int i=0;
		for(Integer p: table.keySet()){
			Vector<Integer> count = table.get(p);
			System.out.println(i+"-predicate:"+p);
			System.out.println(i+"-count:" + count.toString());
			i++;
		}
	}

	public static String getRandomString(){
//		String str="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
//		Random random=new Random();
//		StringBuffer sb=new StringBuffer();
//		for(int i=0;i<8;i++){
//			int number=random.nextInt(36);
//			sb.append(str.charAt(number));
//		}
//		String s = sb.toString();
		return String.valueOf(System.currentTimeMillis());
	}

	public static void main(String[] args){
	}

}
